using System;
namespace RpgServiceLib.Faction
{
	public delegate void ChangedFactionHandler (FactionChangeEvent factionevent);
	
	public class FactionInstance
	{
		public virtual Faction FactionMember { get; private set; }
		public virtual int LevelOfFaction { get; private set; }
	    
	    public virtual int LowestLevelOfFaction { get; private set; }
	    public virtual int HighestLevelOfFaction { get; private set; }
				
		public event ChangedFactionHandler changed;
				
		public FactionInstance (Faction faction, int startWithLevel, int lowestLevel, int highestLevel)
		{
			FactionMember = faction;
			
			this.LowestLevelOfFaction = lowestLevel;
			this.HighestLevelOfFaction = highestLevel;
			
			this.LevelOfFaction = startWithLevel;
			
			CheckLevel ();
		}
		
		public FactionEventStatus IncreaseFaction (int level)
		{	
			FactionEventStatus fes = null;
			
			if (LevelOfFaction + level > HighestLevelOfFaction) {
				level = HighestLevelOfFaction - LevelOfFaction;
			}	
			fes = FireEvent (level, changed != null);

			LevelOfFaction += level;
			
			return fes;
		}

		public FactionEventStatus DecreaseFaction (int level)
		{
			FactionEventStatus fes = null;

			if (LevelOfFaction - level < LowestLevelOfFaction) {
				level = LevelOfFaction - LowestLevelOfFaction;
			}	
			fes = FireEvent (-level, changed != null);

			LevelOfFaction -= level;
			
			return fes;			
		}
		
		public FactionEventStatus GetCurrentEventStatus()
		{
			return  FireEvent(0, false);
		}
		
		public FactionLevelName GetCurrentLevelName ()
		{
			return this.FactionMember.GetFactionLevelNameForLevel (this.LevelOfFaction); 
		}
		
		private FactionEventStatus FireEvent (int level, bool toogleFireEvent)
		{
			FactionEventLevelChangeType changeType = FactionEventLevelChangeType.FactionNotChanged;
			FactionEventLevelNameChanged nameChanged = FactionEventLevelNameChanged.NotChanged;
			FactionEventLevelType levelType = FactionEventLevelType.Normal;
			
			if (level!=0)
			{
				FactionLevelName nameBefore = this.FactionMember.GetFactionLevelNameForLevel (LevelOfFaction);
				
				if (nameBefore != null) {
					FactionLevelName nameNow = this.FactionMember.GetFactionLevelNameForLevel (LevelOfFaction + level);				
					if (nameNow != null && nameBefore != nameNow) {				 
						nameChanged = FactionEventLevelNameChanged.NewNameOfFactionLevel;
					}
				}
			}

			if (level > 0) {
				changeType = FactionEventLevelChangeType.FactionIncreased;
			} else if (level<0) {
				changeType = FactionEventLevelChangeType.FactionDecreased;
			}			
			
 			if (LevelOfFaction + level == LowestLevelOfFaction) {
				levelType = FactionEventLevelType.AtLowestLevel;
			} else if (LevelOfFaction + level == HighestLevelOfFaction) {
				levelType = FactionEventLevelType.AtHighestLevel;
			}			
			
			if (ShouldFireEvent (toogleFireEvent, changeType, nameChanged, levelType))
			{				
				FireEvent(level, new FactionEventStatus(changeType, nameChanged, levelType));									
			}
			
			return new FactionEventStatus(changeType, nameChanged, levelType);
		}

		private FactionChangeEvent CreateFactionChangeEvent (int level, FactionEventStatus eventType)
		{
			return new FactionChangeEvent(eventType, this, this.LevelOfFaction, this.LevelOfFaction + level);
		}
		
		private void FireEvent (int level, FactionEventStatus eventType)
		{
			changed.Invoke(CreateFactionChangeEvent (level, eventType));			
		}	

		bool ShouldFireEvent (bool toogleFireEvent, FactionEventLevelChangeType changeType, FactionEventLevelNameChanged nameChanged, FactionEventLevelType levelType)
		{
			return toogleFireEvent
				&& (
					changeType != FactionEventLevelChangeType.FactionNotChanged
					||
					nameChanged != FactionEventLevelNameChanged.NotChanged
					||
					levelType != FactionEventLevelType.Normal);
		}
		
		private void CheckLevel ()
		{
			if (LowestLevelOfFaction > HighestLevelOfFaction) {
				throw new ArgumentOutOfRangeException ("LowestLevelOfFaction", "LowestLevelOfFaction is higher than HighestLevelOfFaction");
			}
			if (LevelOfFaction > HighestLevelOfFaction) {
				throw new ArgumentOutOfRangeException ("LevelOfFaction", "LevelOfFaction is higher than HighestLevelOfFaction");
			}
			if (LevelOfFaction < LowestLevelOfFaction) {
				throw new ArgumentOutOfRangeException ("LevelOfFaction", "LevelOfFaction is lower than LowestLevelOfFaction");
			}
		}
	}
}

